package org.zhaya.sdk.processors;

import com.google.common.collect.Maps;
import io.logz.sawmill.*;
import io.logz.sawmill.annotations.ProcessorProvider;
import io.logz.sawmill.exceptions.ProcessorConfigurationException;
import io.logz.sawmill.utilities.JsonUtils;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import javax.inject.Inject;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;

@ProcessorProvider(type = "rename", factory = RenameFieldProcessor.Factory.class)
public class RenameFieldProcessor implements Processor {
    private final List<Map<Template, Template>> renameList;
    private final Set<Template> ignoreMissingFieldSet;

    public RenameFieldProcessor(List<Map<Template, Template>> renameList, Set<Template> ignoreMissingFieldSet) {
        this.renameList = requireNonNull(renameList, "cannot work without any renames");
        this.ignoreMissingFieldSet = Objects.nonNull(ignoreMissingFieldSet) ? ignoreMissingFieldSet : Collections.emptySet();
    }

    @Override
    public ProcessResult process(Doc doc) {
        List<String> missingFields = new ArrayList<>();

        for (Map<Template, Template> templateMap : renameList) {
            for (Map.Entry<Template, Template> templateTemplateEntry : templateMap.entrySet()) {
                Template templateEntryKey = templateTemplateEntry.getKey();
                String renderedFrom = templateEntryKey.render(doc);
                if (!doc.hasField(renderedFrom)) {
                    if (!ignoreMissingFieldSet.contains(templateEntryKey)) {
                        missingFields.add(renderedFrom);
                    }
                    continue;
                }
                String renderedTo = templateTemplateEntry.getValue().render(doc);
                Object fieldValue = doc.getField(renderedFrom);
                doc.removeField(renderedFrom);
                doc.addField(renderedTo, fieldValue);
            }
        }

        if (CollectionUtils.isNotEmpty(missingFields)) {
            return ProcessResult.failure(String.format("failed to rename fields [%s], fields are missing", missingFields));
        }

        return ProcessResult.success();
    }

    public static class Factory implements Processor.Factory {

        private final TemplateService templateService;

        @Inject
        public Factory(TemplateService templateService) {
            this.templateService = templateService;
        }

        @Override
        public Processor create(Map<String, Object> config) {
            Object ignoreMissingField = config.get("ignoreMissingField");
            if (Objects.isNull(ignoreMissingField) || !(ignoreMissingField instanceof Boolean)) {
                if ((ignoreMissingField instanceof String) && BooleanUtils.FALSE.equals(ignoreMissingField)) {
                    config.put("ignoreMissingField", false);
                } else {
                    config.put("ignoreMissingField", true);
                }
            }
            Configuration renameFieldConfig = JsonUtils.fromJsonMap(Configuration.class, config);

            if (CollectionUtils.isEmpty(renameFieldConfig.getConfig()) && renameFieldConfig.getConfig() == null && renameFieldConfig.getFrom() == null && renameFieldConfig.getTo() == null) {
                throw new ProcessorConfigurationException("failed to parse rename processor config, couldn't resolve config");
            }

            Set<Template> ignoreMissingFieldSet = new HashSet<>();
            List<Map<Template, Template>> renameList = new ArrayList<>();
            if (StringUtils.isNotEmpty(renameFieldConfig.getTo()) && StringUtils.isNotEmpty(renameFieldConfig.getFrom())) {
                Template fromTemplate = templateService.createTemplate(renameFieldConfig.getFrom());

                Map<Template, Template> renames = Maps.newHashMapWithExpectedSize(2);
                renames.put(fromTemplate, templateService.createTemplate(renameFieldConfig.getTo()));
                renameList.add(renames);
                if (renameFieldConfig.isIgnoreMissingField()) {
                    ignoreMissingFieldSet.add(fromTemplate);
                }
            } else {
                renameList = renameFieldConfig.getConfig().stream().map(renameConfig -> {
                    Map<Template, Template> templateMap = Maps.newHashMapWithExpectedSize(2);
                    Template fromTemplate = templateService.createTemplate(requireNonNull(renameConfig.get("from"), "from cannot be null"));
                    templateMap.put(fromTemplate, templateService.createTemplate(requireNonNull(renameConfig.get("to"), "to cannot be null")));
                    if (!renameConfig.containsKey("ignoreMissingField") || !BooleanUtils.FALSE.equals(renameConfig.get("ignoreMissingField"))) {
                        ignoreMissingFieldSet.add(fromTemplate);
                    }
                    return templateMap;
                }).collect(Collectors.toList());
            }

            return new RenameFieldProcessor(renameList, ignoreMissingFieldSet);
        }
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Configuration implements Processor.Configuration {
        private String from;
        private String to;
        private List<Map<String, String>> config;
        private boolean ignoreMissingField = true;

    }
}